package com.youlu.campus.service.order;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.annotation.JSONField;
import com.google.common.collect.Lists;
import com.youlu.campus.common.utils.HttpClientUtil;
import com.youlu.campus.entity.DeliveryOrderItem;
import com.youlu.campus.entity.VO.req.OrderPostControllerAddressReq;
import com.youlu.campus.entity.ems.EMSBody;
import com.youlu.campus.entity.ems.EMSResult;
import com.youlu.campus.entity.ems.EMSTrace;
import com.youlu.campus.entity.ems.EmsPostControllerResult;
import com.youlu.campus.service.notify.ShunFengService;
import com.youlu.campus.service.order.impl.DeliveryOrderItemService;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 同步EMS
 */
@Slf4j
@Service
public class EMSService {

    @Value("${kuaidi.ems.appId}")
    private String appId;

    @Value("${kuaidi.ems.secrect}")
    private String secret;
    @Value("${kuaidi.ems.url}")
    private String url;

    @Value("${kuaidi.ems.postControllerSecret}")
    private String postControllerSecret;

    @Value("${kuaidi.ems.postControllerAppId}")
    private String postControllerAppId;

    @Value("${kuaidi.ems.postControllerDomain}")
    private String postControllerDomain;


    /**
     * 下单取号接口
     */
    private final static String OMS_ORDER_CREATE_WAYBILLNO_API_CODE = "oms_ordercreate_waybillno";

    private final static String ECOMMERCE_USER_ID = "DREAMBEAT";

    private final static String ECOMMERCE_NO = "CKPS";

    private final static String DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private final static String logistics_provider = "B";

    /**
     * 轨迹查询
     */
    private final static String QPS_QUERYTRACE_API = "qps_querytrace";


    private final static String POST_CONTROLLER_URL = "/iwaybillno-web/a/postController/select";

    @Autowired
    private DeliveryOrderItemService deliveryOrderItemService;

    @Autowired
    private ShunFengService shunFengService;


    public EmsPostControllerResult doEmsPostController(OrderPostControllerAddressReq orderPostControllerAddressReq) {
        String orderNo = orderPostControllerAddressReq.getOrderNo();
        log.info("订单地址管控 查询 EMSService.doEmsPostController orderNo ->{} start", orderNo);
        RequestOrder requestOrder = new RequestOrder();
        requestOrder.setEcommerceNo(ECOMMERCE_USER_ID);
        requestOrder.setReceiverProvinceName(orderPostControllerAddressReq.getReceiverProvinceName());
        requestOrder.setReceiverCityName(orderPostControllerAddressReq.getReceiverCityName());
        requestOrder.setReceiverCountyName(orderPostControllerAddressReq.getReceiverCountyName());
        requestOrder.setReceiverAddr(orderPostControllerAddressReq.getReceiverAddr());
        requestOrder.setSenderNo(appId);
        PostController jsonPost = new PostController();
        jsonPost.setRequestOrder(requestOrder);
        cn.hutool.json.JSON parse = JSONUtil.parse(jsonPost);
        String xml = JSONUtil.toXmlStr(parse);
        Map<String, Object> paramMap = new HashMap();
        paramMap.put("partnered", postControllerSecret);
        paramMap.put("ecCompanyId", ECOMMERCE_USER_ID);
        paramMap.put("msg_type", "POSTCONTROLLER");
        paramMap.put("logistics_interface", xml);
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            BASE64Encoder base64en = new BASE64Encoder();
            String signature = base64en.encode(md5.digest((xml + postControllerSecret).getBytes("UTF-8")));
            paramMap.put("data_digest", signature);
            String result = HttpClientUtil.postForm(postControllerDomain + POST_CONTROLLER_URL,
                    paramMap, null, 3000, "utf8", 3, true);
            log.info("订单地址管控 返回结果 EMSService.doEmsPostController orderNo ->{} result ->{}", orderNo, result);
            JSONObject jsonObject = JSONUtil.xmlToJson(result);
            String xmlToJsonStr = jsonObject.toStringPretty();
            EMSResult<EmsPostControllerResult> emsResult = JSON.parseObject(xmlToJsonStr,
                    new TypeReference<EMSResult<EmsPostControllerResult>>() {
            });
            if (Objects.isNull(emsResult)) {
                return null;
            }
            EmsPostControllerResult postControllerResult = emsResult.getResponse();
            if (Objects.isNull(postControllerResult)) {
                return null;
            }
            log.info("订单地址管控 返回结果 EMSService.doEmsPostController orderNo ->{} success ->{} code -> {} msg ->{}",
                    orderNo,
                    postControllerResult.getSuccess(), postControllerResult.getErrorCode(),
                    postControllerResult.getErrorMsg());

            log.info("订单地址管控 查询 EMSService.doEmsPostController orderNo ->{} end", orderNo);
            return postControllerResult;
        } catch (IOException e) {
            e.printStackTrace();
            log.error("订单地址管控 查询 EMSService.doEmsPostController orderNo ->{} error", orderNo, e);
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("订单地址管控 查询 EMSService.doEmsPostController orderNo ->{} error", orderNo, e);
            return null;
        }
    }

    /**
     * ems运单号轨迹查询
     */
    public void doHandleEmsbillNoTraceJob() {
        log.info("【定时查询订单轨迹】start");
        List<DeliveryOrderItem> itemList =
                deliveryOrderItemService.findByStatusAndDeliveryStatus(DeliveryOrderItem.SUCCESS, "delivery");
        if (CollectionUtils.isEmpty(itemList)) {
            return;
        }
        itemList.parallelStream().forEach(deliveryOrderItem -> {
            if (StringUtils.isNotBlank(deliveryOrderItem.getExpressNumber())) {
                this.queryTrace(deliveryOrderItem.getSubOrderNo(), deliveryOrderItem.getExpressNumber());
            }
        });
        log.info("【定时查询订单轨迹】end");
    }


    @Async
    public void doEms(DeliveryOrderItem deliveryOrderItem) {
        log.info("【同步单号EMS】start 同步订单号 ->{}", deliveryOrderItem.getOrderNo());
        OrderNormal orderNormal = buildOrder(deliveryOrderItem);
        LogisticsInterface logisticsInterface = new LogisticsInterface();
        logisticsInterface.setOrderNormal(orderNormal);
        String logisticsInterfaceJSON = JSON.toJSONString(logisticsInterface);
        log.info("logisticsInterface ->{}", logisticsInterfaceJSON);
        EMSSign emsSign = new EMSSign();
        try {
            String signature =
                    Base64.getEncoder().encodeToString(emsSign.getSHA256(logisticsInterfaceJSON + secret).getBytes(
                            "UTF-8"));
            log.info("signature ->{}", signature);
            Map<String, Object> paramMap = new HashMap(8);
            paramMap.put("apiCode", OMS_ORDER_CREATE_WAYBILLNO_API_CODE);
            paramMap.put("senderNo", appId);
            paramMap.put("serialNo", IdUtil.getSnowflake().nextIdStr());
            paramMap.put("signature", signature);
            paramMap.put("msgType", 0);
            paramMap.put("logistics_interface", logisticsInterfaceJSON);
            String result = HttpClientUtil.postForm(url, paramMap, null, 3000, "utf8", 3, true);
            log.info(" ems orderNo ->{} result ->{}", deliveryOrderItem.getSubOrderNo(), result);
            if (StringUtils.isBlank(result)) {
                return;
            }
            EMSResult<EMSBody> emsResult = JSON.parseObject(result, new TypeReference<EMSResult<EMSBody>>() {
            });
            if (Objects.isNull(emsResult)) {
                return;
            }
            String taskId = deliveryOrderItem.getTaskId();
            deliveryOrderItemService.statisticsTask(taskId, "count", 1);
            EMSBody emsBody = emsResult.getBody();
            if (!EMSResult.SUCCESS_CODE.equals(emsResult.getCode()) || Objects.isNull(emsBody)) {
                log.error("【EMS】ems push orderNo ->{} code ->{} msg->{}", deliveryOrderItem.getSubOrderNo(),
                        emsResult.getCode(),
                        emsResult.getMessage());
                //统计条数
                deliveryOrderItemService.statisticsTask(taskId, "failCount", 1);
                deliveryOrderItemService.updateItem(deliveryOrderItem.getId(), "", DeliveryOrderItem.FAIL);
                return;
            }
            String waybillNo = emsBody.getWaybillNo();
            //统计条数
            deliveryOrderItemService.statisticsTask(deliveryOrderItem.getTaskId(), "successCount", 1);
            deliveryOrderItemService.updateItem(deliveryOrderItem.getId(), waybillNo, DeliveryOrderItem.SUCCESS);
            //更新订单状态
            deliveryOrderItemService.updateOrder(deliveryOrderItem.getSubOrderNo(), waybillNo);
            log.info("【EMS】ems push orderNo ->{} code ->{} waybillNo->{}", deliveryOrderItem.getSubOrderNo(),
                    emsResult.getCode(),
                    waybillNo);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询物流单号轨迹
     *
     * @param waybillNo
     */
    public void queryTrace(String subOrderNo, String waybillNo) {
        log.info("【EMS-轨迹查询】ems orderNo ->{} | waybillNo->{}", subOrderNo, waybillNo);
        if (StringUtils.isBlank(subOrderNo) || StringUtils.isBlank(waybillNo)) {
            return;
        }
        QueryTrace queryTrace = new QueryTrace();
        queryTrace.setWaybillNo(waybillNo);
        String waybillNoJSON = JSON.toJSONString(queryTrace);
        EMSSign emsSign = new EMSSign();
        try {
            String signature = Base64.getEncoder().encodeToString(emsSign.getSHA256(waybillNoJSON + secret).getBytes(
                    "UTF-8"));
            Map<String, Object> paramMap = new HashMap(8);
            paramMap.put("apiCode", QPS_QUERYTRACE_API);
            paramMap.put("senderNo", appId);
            paramMap.put("serialNo", IdUtil.getSnowflake().nextIdStr());
            paramMap.put("signature", signature);
            paramMap.put("msgType", 0);
            paramMap.put("logistics_interface", waybillNoJSON);
            String result = HttpClientUtil.postForm(url, paramMap, null, 3000, "utf8", 3, true);
            log.info("【EMS-轨迹查询】 ems orderNo ->{} result ->{}", subOrderNo, result);
            if (StringUtils.isBlank(result)) {
                return;
            }
            EMSResult<List<EMSTrace>> emsResult = JSON.parseObject(result,
                    new TypeReference<EMSResult<List<EMSTrace>>>() {
                    });
            if (Objects.isNull(emsResult)) {
                return;
            }
            List<EMSTrace> emsTraceList = emsResult.getBody();
            if (!EMSResult.SUCCESS_CODE.equals(emsResult.getCode()) || CollectionUtils.isEmpty(emsTraceList)) {
                log.error("【EMS-轨迹查询】 查询错误 ems orderNo ->{} | waybillNo->{}| code->{} | message ->{}", subOrderNo,
                        waybillNo
                        , emsResult.getCode(), emsResult.getMessage());
                return;
            }
            shunFengService.updateOrder(subOrderNo, waybillNo);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("【EMS-轨迹查询】ems orderNo ->{} | waybillNo->{} error", subOrderNo, waybillNo, e);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("【EMS-轨迹查询】ems orderNo ->{} | waybillNo->{} error", subOrderNo, waybillNo, e);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("【EMS-轨迹查询】ems orderNo ->{} | waybillNo->{} error", subOrderNo, waybillNo, e);
        }
    }

    @Getter
    @Setter
    class RequestOrder {
        /**
         * 来源系统
         */
        private String ecommerceNo;

        private String baseProductNo = "1";
        /**
         * 1.文件 2.物品
         */
        private String contentsAttribute = "1";

        /**
         * 寄达地省
         */
        private String receiverProvinceName;

        /**
         * 寄达地市
         */
        private String receiverCityName;

        /**
         * 寄达地县
         */
        private String receiverCountyName;

        /**
         * 寄达地详细地址
         */
        private String receiverAddr;

        /**
         * 发件地省
         */
        private final String senderProvinceName = "天津市";

        /**
         * 发件地市
         */
        private final String senderCityName = "天津市";
        ;

        /**
         * 发件地县
         */
        private final String senderCountyName = "滨海新区";

        /**
         * 发件地详细地址
         */
        private final String senderAddr = "大港学院路学院桥";

        /**
         * 客户代码
         */
        private String senderNo;

        private String weight;

    }

    @Getter
    @Setter
    class PostController {
        @JSONField(name = "RequestOrder")
        private RequestOrder RequestOrder;
    }


    @Getter
    @Setter
    class QueryTrace {
        @JSONField(name = "waybill_no")
        private String waybillNo;

    }

    @Getter
    @Setter
    class LogisticsInterface {
        @JSONField(name = "OrderNormal")
        private OrderNormal orderNormal;
    }

    private OrderNormal buildOrder(DeliveryOrderItem deliveryOrderItem) {
        OrderNormal orderNormal = new OrderNormal();
        orderNormal.setSenderNo(appId);
        orderNormal.setEcommerceUserId(ECOMMERCE_USER_ID);
        orderNormal.setSenderNo(appId);
        orderNormal.setLogisticsOrderNo(deliveryOrderItem.getSubOrderNo());
        orderNormal.setCreatedTime(deliveryOrderItem.getTimeEMS().format(DateTimeFormatter.ofPattern(DATA_FORMAT)));
        orderNormal.setLogisticsProvider(logistics_provider);
        orderNormal.setEcommerceNo(ECOMMERCE_NO);
        orderNormal.setBatchNo(deliveryOrderItem.getBatchNo());
        orderNormal.setSender(new Sender("赞我服务中心", "18518002032", "天津市", "天津市", "滨海新区", "大港学院路学院桥"));
        orderNormal.setReceiver(new Receiver(deliveryOrderItem.getReceiveName(), deliveryOrderItem.getReceiveMobile(),
                deliveryOrderItem.getProvince(), deliveryOrderItem.getCity(), deliveryOrderItem.getCounty(),
                deliveryOrderItem.getAddress()));

        List<String> remarkList = new ArrayList<>(5);
        String certificateNumber = deliveryOrderItem.getCertificateNumber();
        String itemName = deliveryOrderItem.getItemName();
        if (StringUtils.isNotBlank(itemName)) {
            remarkList.add(itemName);
        }
        if (StringUtils.isNotBlank(deliveryOrderItem.getYaXian())) {
            remarkList.add(deliveryOrderItem.getYaXian());
        }
        if (StringUtils.isNotBlank(deliveryOrderItem.getCaPrintName())) {
            remarkList.add(deliveryOrderItem.getCaPrintName());
        }
        if (StringUtils.isNotBlank(certificateNumber)) {
            remarkList.add(certificateNumber);
        }
        if (StringUtils.isNotBlank(deliveryOrderItem.getActivityName())) {
            remarkList.add(deliveryOrderItem.getActivityName());
        }
        orderNormal.setPickupNotes(remarkList.stream().collect(Collectors.joining(",")));
        orderNormal.setCargos(buildCargo(deliveryOrderItem));
        return orderNormal;
    }

    private List<Cargo> buildCargo(DeliveryOrderItem deliveryOrderItem) {
        List<Cargo> cargos = Lists.newArrayList();
        if (StringUtils.isNotBlank(deliveryOrderItem.getCertificateName())) {
            Cargo cargo = new Cargo();
            cargo.setCargoName(deliveryOrderItem.getCertificateName());
            cargo.setCargoQuantity(1);
            //cargo.setCargoWeight("本");
            cargos.add(cargo);
        }
        if (StringUtils.isNotBlank(deliveryOrderItem.getItemName())) {
            Cargo cargo = new Cargo();
            cargo.setCargoName(deliveryOrderItem.getItemName());
            cargo.setCargoQuantity(1);
            // cargo.setCargoWeight("个");
            cargos.add(cargo);
        }
        return cargos;
    }

    @Getter
    @Setter
    class OrderNormal {


        /**
         * 订单接入时间-必传
         */
        @JSONField(name = "created_time")
        private String createdTime;

        /**
         * 电商客户标识-必传
         */
        @JSONField(name = "ecommerce_user_id")
        private String ecommerceUserId;

        /**
         * 协议客户代码
         */
        @JSONField(name = "sender_no")
        private String senderNo;


        /**
         * 物流订单号-必传
         */
        @JSONField(name = "logistics_order_no")
        private String logisticsOrderNo;


        @JSONField(name = "biz_product_no")
        private String bizProductNo;

        @JSONField(name = "submail_no")
        private String submailNo;

        @JSONField(name = "waybill_no")
        private String waybillNo;

        @JSONField(name = "batch_no")
        private String batchNo;

        /**
         * 一票多件标识
         */
        @JSONField(name = "one_bill_flag")
        private int oneBillFlag = 0;

        /**
         * 内部订单来源标识
         * 0：直接对接 1：邮务国内小包订单系统
         * 2：邮务国际小包订单系统
         * 3：速递国内订单系统
         * 4：速递国际订单系统（shipping）
         * 5：在线发货平台------默认为’0’
         */
        @JSONField(name = "inner_channel")
        private int innerChannel = 0;

        /**
         * 物流承运方-必传
         */
        @JSONField(name = "logistics_provider")
        private String logisticsProvider;

        /**
         * 渠道来源标识-必传
         */
        @JSONField(name = "ecommerce_no")
        private String ecommerceNo;

        /**
         * 1：文件  3、物品
         * 2：信函  4、包裹
         */
        @JSONField(name = "contents_attribute")
        private int contentsAttribute = 3;

        /**
         * 基础产品代码
         * 1：标准快递  2：快递包裹
         * 3：代收/到付（标准快递）
         */
        @JSONField(name = "base_product_no")
        private String baseProductNo = "1";
        /**
         * 寄见人信息
         */
        private Sender sender;
        /**
         * 收货人信息
         */
        private Receiver receiver;

        private List<Cargo> cargos;

        private Double weight;

        private Double volume;

        private Double length;

        private Double width;

        private Double height;

        /**
         * 邮费
         */
        @JSONField(name = "postage_total")
        private Double postage_total;

        /**
         * 1:基本2:保价3:保险
         */
        @JSONField(name = "insurance_flag")
        private int insuranceFlag = 1;

        /**
         * 保价金额
         */
        @JSONField(name = "insurance_amount")
        private Double insuranceAmount;

        /**
         * 保险金额
         */
        @JSONField(name = "insurance_premium_amount")
        private Double insurancePremiumAmount;


        /**
         * 投递方式
         * 1:客户自提2:上门投递3:智能包裹柜4:网点代投
         */
        @JSONField(name = "deliver_type")
        private int deliverType;

        /**
         * 投递预约开始时间
         */
        @JSONField(name = "deliver_pre_date")
        private String deliverPreDate;

        /**
         * 揽收方式：0 客户送货上门，1 机构上门揽收
         */
        @JSONField(name = "pickup_type")
        private int pickupType;

        /**
         * 揽收预约起始时间 yyyy-mm-dd hh:mm:ss
         */
        @JSONField(name = "pickup_pre_begin_time")
        private String pickup_pre_begin_time;

        /**
         * 揽收预约截至时间 yyyy-mm-dd hh:mm:ss
         */
        @JSONField(name = "pickup_pre_end_time")
        private String pickup_pre_end_time;

        /**
         * 付款方式
         * 1:寄件人 2:收件人 3:第三方 4:收件人集中付费 5:免费 6:寄/收件人 7:预付卡
         */
        @JSONField(name = "payment_mode")
        private int paymentMode = 1;

        /**
         * 代收款标志
         * 1:代收货款2:代缴费9:无
         */
        @JSONField(name = "cod_flag")
        private int codFlag = 9;
        /**
         * 代收款金额
         */
        @JSONField(name = "cod_amount")
        private Double cod_amount;

        /**
         * 回单标识
         * 1:基本2:回执 3:短信 5:电子返单
         * 6:格式返单7:自备返单8:反向返单
         */
        @JSONField(name = "receipt_flag")
        private int receiptFlag = 1;

        /**
         * 回单运单号
         */
        @JSONField(name = "receipt_waybill_no")
        private String receiptWaybillNo;

        /**
         * 电子优惠券号
         */
        @JSONField(name = "electronic_preferential_no")
        private String electronicPreferentialNo;

        /**
         * 电子优惠券金额
         */
        @JSONField(name = "electronic_preferential_amount")
        private Double electronicPreferentialAmount;

        /**
         * 贵品标识:0 无 1有
         */
        @JSONField(name = "valuable_flag")
        private int valuable_flag = 0;
        /**
         * 备注-显示在面单备注
         */
        @JSONField(name = "pickup_notes")
        private String pickupNotes;
        @JSONField(name = "sender_safety_code")
        private String senderSafetyCode;
        @JSONField(name = "receiver_safety_code")
        private String receiverSafetyCode;
        private String note;
    }

    @Getter
    @Setter
    final class Sender extends EmsAddress {
        public Sender(String name, String mobile, String prov, String city, String county, String address) {
            this.setName(name);
            this.setMobile(mobile);
            this.setProv(prov);
            this.setCity(city);
            this.setCounty(county);
            this.setAddress(address);
        }
    }

    @Getter
    @Setter
    class Receiver extends EmsAddress {

        public Receiver(String name, String mobile, String prov, String city, String county, String address) {
            this.setName(name);
            this.setMobile(mobile);
            this.setProv(prov);
            this.setCity(city);
            this.setCounty(county);
            this.setAddress(address);
        }
    }

    @Getter
    @Setter
    class Cargo {

        @JSONField(name = "cargo_name")
        private String cargoName;

        @JSONField(name = "cargo_category")
        private String cargoCategory;

        @JSONField(name = "cargo_quantity")
        private int cargoQuantity;

        @JSONField(name = "cargo_value")
        private String cargoValue;

        @JSONField(name = "cargo_weight")
        private String cargoWeight;
        @JSONField(name = "order_no")
        private String orderNo;

        @JSONField(name = "length")
        private int length;
        @JSONField(name = "width")
        private int width;
        @JSONField(name = "high")
        private int high;
    }


    @Getter
    @Setter
    class EmsAddress {

        /**
         * 姓名
         */
        private String name;

        private String mobile;

        private String prov;

        private String city;

        private String county;

        private String address;
        /**
         * 户电话，包括区号、电话号码及分机号，中间用“-”分隔；
         */
        private String phone;

        /**
         * 用户邮编
         */
        @JSONField(name = "post_code")
        private String postCode;
    }


    class EMSSign {

        public String getSHA256(String str) {
            MessageDigest messageDigest;
            String encodestr = "";
            try {
                messageDigest = MessageDigest.getInstance("SHA-256");
                messageDigest.update(str.getBytes("UTF-8"));
                encodestr = byte2Hex(messageDigest.digest());
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return encodestr;
        }

        private String byte2Hex(byte[] bytes) {
            StringBuffer stringBuffer = new StringBuffer();
            String temp = null;
            for (int i = 0; i < bytes.length; i++) {
                temp = Integer.toHexString(bytes[i] & 0xFF);
                if (temp.length() == 1) {
                    // 1得到一位的进行补0操作
                    stringBuffer.append("0");
                }
                stringBuffer.append(temp);
            }
            return stringBuffer.toString();
        }

    }


}
